From a3538e25e8c6855e9c33bab1fe79a667cecd87b1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 12 Nov 2014 22:15:41 -0800 Subject: [PATCH] Implement a command to list owners of a crate Closes #858 --- src/bin/owner.rs | 17 +++++++++------ src/cargo/ops/mod.rs | 2 +- src/cargo/ops/registry.rs | 45 ++++++++++++++++++++++++++++----------- src/registry/lib.rs | 42 +++++++++++++++++++++++++++--------- 4 files changed, 77 insertions(+), 29 deletions(-) diff --git a/src/bin/owner.rs b/src/bin/owner.rs index efa1d9ba7..8c64ecbfd 100644 --- a/src/bin/owner.rs +++ b/src/bin/owner.rs @@ -11,6 +11,7 @@ struct Options { flag_remove: Option>, flag_index: Option, flag_verbose: bool, + flag_list: bool, } pub const USAGE: &'static str = " @@ -23,6 +24,7 @@ Options: -h, --help Print this message -a, --add LOGIN Login of a user to add as an owner -r, --remove LOGIN Login of a user to remove as an owner + -l, --list List owners of a crate --index INDEX Registry index to modify owners for --token TOKEN API token to use when authenticating -v, --verbose Use verbose output @@ -35,12 +37,15 @@ versions, and also modify the set of owners, so take caution! pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult> { shell.set_verbose(options.flag_verbose); let root = try!(find_root_manifest_for_cwd(None)); - try!(ops::modify_owners(&root, shell, - options.arg_crate, - options.flag_token, - options.flag_index, - options.flag_add, - options.flag_remove).map_err(|e| { + let opts = ops::OwnersOptions { + krate: options.arg_crate, + token: options.flag_token, + index: options.flag_index, + to_add: options.flag_add, + to_remove: options.flag_remove, + list: options.flag_list, + }; + try!(ops::modify_owners(&root, shell, &opts).map_err(|e| { CliError::from_boxed(e, 101) })); Ok(None) diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index 5a083bda1..885a15e2b 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -18,7 +18,7 @@ pub use self::cargo_test::{run_tests, run_benches, TestOptions}; pub use self::cargo_package::package; pub use self::registry::{publish, registry_configuration, RegistryConfig}; pub use self::registry::{registry_login, http_proxy, http_handle}; -pub use self::registry::{modify_owners, yank}; +pub use self::registry::{modify_owners, yank, OwnersOptions}; pub use self::cargo_fetch::{fetch}; pub use self::cargo_pkgid::pkgid; pub use self::resolve::{resolve_pkg, resolve_with_previous}; diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index f5e994457..16f5f6397 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -218,15 +218,20 @@ pub fn registry_login(shell: &mut MultiShell, token: String) -> CargoResult<()> config::set_config(&config, config::Global, "registry", config::Table(map)) } +pub struct OwnersOptions { + pub krate: Option, + pub token: Option, + pub index: Option, + pub to_add: Option>, + pub to_remove: Option>, + pub list: bool, +} + pub fn modify_owners(manifest_path: &Path, shell: &mut MultiShell, - krate: Option, - token: Option, - index: Option, - to_add: Option>, - to_remove: Option>) -> CargoResult<()> { - let name = match krate { - Some(name) => name, + opts: &OwnersOptions) -> CargoResult<()> { + let name = match opts.krate { + Some(ref name) => name.clone(), None => { let mut src = try!(PathSource::for_path(&manifest_path.dir_path())); try!(src.update()); @@ -235,10 +240,11 @@ pub fn modify_owners(manifest_path: &Path, } }; - let (mut registry, _) = try!(registry(shell, token, index)); + let (mut registry, _) = try!(registry(shell, opts.token.clone(), + opts.index.clone())); - match to_add { - Some(v) => { + match opts.to_add { + Some(ref v) => { let v = v.iter().map(|s| s.as_slice()).collect::>(); try!(shell.status("Owner", format!("adding `{:#}` to `{}`", v, name))); try!(registry.add_owners(name.as_slice(), v.as_slice()).map_err(|e| { @@ -248,8 +254,8 @@ pub fn modify_owners(manifest_path: &Path, None => {} } - match to_remove { - Some(v) => { + match opts.to_remove { + Some(ref v) => { let v = v.iter().map(|s| s.as_slice()).collect::>(); try!(shell.status("Owner", format!("removing `{:#}` from `{}`", v, name))); @@ -260,6 +266,21 @@ pub fn modify_owners(manifest_path: &Path, None => {} } + if opts.list { + let owners = try!(registry.list_owners(name.as_slice()).map_err(|e| { + human(format!("failed to list owners: {}", e)) + })); + for owner in owners.iter() { + print!("{}", owner.login); + match (owner.name.as_ref(), owner.email.as_ref()) { + (Some(name), Some(email)) => println!(" ({} <{}>)", name, email), + (Some(s), None) | + (None, Some(s)) => println!(" ({})", s), + (None, None) => println!(""), + } + } + } + Ok(()) } diff --git a/src/registry/lib.rs b/src/registry/lib.rs index 2c5938c6b..9c58160bf 100644 --- a/src/registry/lib.rs +++ b/src/registry/lib.rs @@ -8,6 +8,7 @@ use std::io::util::ChainedReader; use std::result; use curl::http; +use curl::http::handle::{Put, Get, Delete, Method, Request}; use serialize::json; pub struct Registry { @@ -53,10 +54,20 @@ pub struct NewCrateDependency { pub target: Option, } +#[deriving(Decodable)] +pub struct User { + pub id: uint, + pub login: String, + pub avatar: String, + pub email: Option, + pub name: Option, +} + #[deriving(Decodable)] struct R { ok: bool } #[deriving(Decodable)] struct ApiErrorList { errors: Vec } #[deriving(Decodable)] struct ApiError { detail: String } #[deriving(Encodable)] struct OwnersReq<'a> { users: &'a [&'a str] } +#[deriving(Decodable)] struct Users { users: Vec } impl Registry { pub fn new(host: String, token: String) -> Registry { @@ -88,6 +99,11 @@ impl Registry { Ok(()) } + pub fn list_owners(&mut self, krate: &str) -> Result> { + let body = try!(self.get(format!("/crates/{}/owners", krate))); + Ok(json::decode::(body.as_slice()).unwrap().users) + } + pub fn publish(&mut self, krate: &NewCrate, tarball: &Path) -> Result<()> { let json = json::encode(krate); // Prepare the body. The format of the upload request is: @@ -135,19 +151,25 @@ impl Registry { } fn put(&mut self, path: String, b: &[u8]) -> Result { - handle(self.handle.put(format!("{}/api/v1{}", self.host, path), b) - .header("Authorization", self.token.as_slice()) - .header("Accept", "application/json") - .content_type("application/json") - .exec()) + self.req(path, Some(b), Put) + } + + fn get(&mut self, path: String) -> Result { + self.req(path, None, Get) } fn delete(&mut self, path: String, b: Option<&[u8]>) -> Result { - let mut req = self.handle.delete(format!("{}/api/v1{}", self.host, path)) - .header("Authorization", self.token.as_slice()) - .header("Accept", "application/json") - .content_type("application/json"); - match b { + self.req(path, b, Delete) + } + + fn req(&mut self, path: String, body: Option<&[u8]>, + method: Method) -> Result { + let mut req = Request::new(&mut self.handle, method) + .uri(format!("{}/api/v1{}", self.host, path)) + .header("Authorization", self.token.as_slice()) + .header("Accept", "application/json") + .content_type("application/json"); + match body { Some(b) => req = req.body(b), None => {} } -- 2.30.2